#if !defined(ximera_input_idevice_hpp)
#define ximera_input_idevice_hpp

// -----------------------------------------------------------

#include <ximera/types.hpp>
#include <ximera/common/sharedptr.hpp>
#include <ximera/enum.hpp>
#include <ximera/common/signal.hpp>
#include <ximera/system/isystem.hpp>

namespace ximera { namespace input
{
    //! \brief Exception for Device errors
    XIMERA_DECLARE_EXCEPTION(DeviceException, L"Device error");

    //! Interface that represents an input device (Keyboard, Mouse, Gamepad, etc.)
    class XIMERA_API IDevice
    {
    public:
        /*! Defines the prototype of the event that will be called when a axis value was changed on the device.
         *  \param sender It's the device where the axis value was changed.
         *  \param axisIndex It's the index of the axis that its value changed.
         *  \param lastValue It's the previous value of the axis.
         *  \param newValue It's the new value of the axis.
         *  \param relValue It's the relative value betwen the lastValue and the newValue. */
        typedef void (OnAxisChangeEvent)(IDevice* sender, Byte axisIndex, Real lastValue, Real newValue, Real relValue);

        /*! Defines the prototype of the event that will be called when a axis value was changed on the device.
         *  \param sender It's the device where the axis value was changed.
         *  \param axisIndex It's the index of the axis that its value changed.
         *  \param lastValue It's the previous value of the axis.
         *  \param newValue It's the new value of the axis.
         *  \param relValue It's the relative value betwen the lastValue and the newValue. */
        typedef void (OnAxisIntChangeEvent)(IDevice* sender, Byte axisIndex, Int16 lastValue, Int16 newValue, Int16 relValue);

        /*! Defines the prototype of the event that will be called when a button value was changed on the device.
         *  \param sender It's the device where the button value was changed.
         *  \param axisIndex It's the index of the button that its value changed.
         *  \param lastValue It's the previous value of the button.
         *  \param newValue It's the new value of the button.
         *  \param relValue It's the relative value betwen the lastValue and the newValue. */
        typedef void (OnButtonChangeEvent)(IDevice* sender, Byte buttonIndex, Byte lastValue, Byte newValue, Int16 relValue);

        /*! Defines the prototype of the event that will be called when a button is pressed on the device.
         *  \param sender It's the device where the button was pressed.
         *  \param keyCode It's the button that was pressed. */
        typedef void (OnButtonDownEvent)(IDevice* sender, Byte buttonIndex);

        /*! Defines the prototype of the event that will be called when a button is released on the device.
         *  \param sender It's the device where the button was released.
         *  \param keyCode It's the button that was released. */
        typedef void (OnButtonUpEvent)(IDevice* sender, Byte buttonIndex);

        IDevice();

        virtual ~IDevice();

        //! \brief Returns how many axis contains this device
        virtual Byte axisCount() = 0;

        /*! \brief Returns the dead zone of the axis.
         *
         * The dead zone is the value from which it will start to detect movement of the axis
         */
        virtual Real axisDeadZone();

        /*! \brief Sets the dead zone of the axis.
         *
         * The dead zone is the value from which it will start to detect movement of the axis
         *  \param deadZone The value to be asigned to the dead zone
         */
        virtual void axisDeadZone(Real deadZone);

        /*! \brief Returns the dead zone of the axis.
         *
         * The dead zone is the value from which it will start to detect movement of the axis
         */
        virtual Int16 axisDeadZoneInt();

        /*! \brief Sets the dead zone of the axis.
         *
         * The dead zone is the value from which it will start to detect movement of the axis
         *  \param deadZone The value to be asigned to the dead zone
         */
        virtual void axisDeadZoneInt(Int16 deadZone);

        /*! \brief Returns the threshold of the axis.  
         *
         * When a movement of the axis exceeds this value, the event onAxisChange will be thrown.
         */
        virtual Real axisThreshold();

        /*! \brief Sets the thresholld of the axis.
         *
         * When a movement of the axis exceeds this value, the event onAxisChange will be thrown.
         *  \param threshold The value to be asigned to the dead zone
         */
        virtual void axisThreshold(Real threshold);

        /*! \brief Returns the threshold of the axis.  
         *
         * When a movement of the axis exceeds this value, the event onAxisChange will be thrown.
         */
        virtual Int16 axisThresholdInt();

        /*! \brief Sets the thresholld of the axis.
         *
         * When a movement of the axis exceeds this value, the event onAxisChange will be thrown.
         *  \param threshold The value to be asigned to the dead zone
         */
        virtual void axisThresholdInt(Int16 threshold);

        /*! \brief Returns the current absolute value of an axis as a Real value.
         *
         *  The returned values will be a Real value between -1.0 and 1.0
         *  \param axisIndex Is the index of the axis that is wanted 
         */
        virtual Real axis(Byte axisIndex) = 0;

        /*! \brief Returns the current absolute value of an axis as an Int16 value.
         *
         *  The returned values will be an Int16 value between -32768 and 32767
         *  \param axisIndex Is the index of the axis that is wanted 
         */
        virtual Int16 axisInt(Byte axisIndex) = 0;

        /*! \brief Returns the current relative value of an axis as a Real value.
         *
         *  The returned values will be a Real value between -1.0 and 1.0
         *  \param axisIndex Is the index of the axis that is wanted 
         */
        virtual Real axisRel(Byte axisIndex) = 0;

        /*! \brief Returns the current relative value of an axis as an Int16 value.
         *
         *  The returned values will be an Int16 value between -32768 and 32767
         *  \param axisIndex Is the index of the axis that is wanted 
         */
        virtual Int16 axisRelInt(Byte axisIndex) = 0;

        //! \brief Returns how many buttons contains this device
        virtual UInt16 buttonCount() = 0;

        /*! \brief Returns the dead zone of the button.
         *
         * The button dead zone is the value from which it will detect the button ass pressed
         */
        virtual Byte buttonDeadZone();

        /*! \brief Sets the dead zone of the axis.
         *
         * The button dead zone is the value from which it will detect the button ass pressed
         *  \param deadZone The value to be asigned to the dead zone
         */
        virtual void buttonDeadZone(Byte deadZone);

        /*! \brief Returns the threshold of the button.
         *
         * When the pressure of the button exceeds this value, the event onButtonChange will be thrown.
         */
        virtual Byte buttonThreshold();

        /*! \brief Sets the thresholld of the axis.
         *
         * When the pressure of the button exceeds this value, the event onButtonChange will be thrown.
         *  \param threshold The value to be asigned to the dead zone
         */
        virtual void buttonThreshold(Byte threshold);

        /*! \brief Returns the current value of a button.
         *
         *  The returned values will be between 0 and 255, where 0 is release and 255 full pressed.
         *  \param buttonIndex Is the index of the button that is wanted 
         */
        virtual Byte button(UInt16 buttonIndex) = 0;

        /*! \brief Returns if the button is pressed.
         *
         *  Returns true if the button is pressed, otherwise returns false.
         *  \param buttonIndex Is the index of the button that is wanted 
         */
        virtual bool isPressed(UInt16 buttonIndex);

        //! \brief Returns how many rumble motors contains this device
        virtual Byte rumbleCount() = 0;

        /*! \brief Returns the current rumble value.
         *
         *  Returns the rummble value of the specified rumble motor. The returned value will be between 0 (motor off) and 65535 (full rumble).
         *  \param rumbleIndex Is the index of the rumble motor that is wanted 
         */
        virtual UInt16 rumble(Byte rumbleIndex) const = 0;

        /*! \brief Sets the current rumble value.
         *
         *  Sets the rummble value of the specified rumble motor. The value must be between 0 (motor off) and 65535 (full rumble).
         *  \param rumbleIndex Is the index of the rumble motor that is wanted 
         *  \param value Is the rumble level to be set 
         */
        virtual void rumble(Byte rumbleIndex, UInt16 value) = 0;

        /*! Event that rises when the value of an axis changes. */
        Signal<OnAxisChangeEvent>& onAxisChange();

        /*! Event that rises when the value of an axis changes. */
        Signal<OnAxisIntChangeEvent>& onAxisIntChange();

        /*! Event that rises when the value of a button changes. */
        Signal<OnButtonChangeEvent>& onButtonChange();

        /*! Event that rises when a button is pressed to the IDevice. */
        Signal<OnButtonDownEvent>& onButtonDown();

        /*! Event that rises when a button is released to the IDevice. */
        Signal<OnButtonUpEvent>& onButtonUp();

    protected:

        Signal<OnAxisChangeEvent> _onAxisChange;
        Signal<OnAxisIntChangeEvent> _onAxisIntChange;
        Signal<OnButtonChangeEvent> _onButtonChange;
        Signal<OnButtonDownEvent> _onButtonDown;
        Signal<OnButtonUpEvent> _onButtonUp;

        Int16 _deadZoneInt, _thresholdInt;
        Real _deadZone, _threshold;
        Byte _buttonDeadZone, _buttonThreshold;
    };
}}

// -----------------------------------------------------------

#endif
